package br.usjt.mazerobot.commons.comunicacao;

import br.usjt.mazerobot.commons.constante.IdentificadorPacote;
import br.usjt.mazerobot.commons.navegacao.Celula;

public class Encoder {

    //======================= Codifica Pacote ==================================
    public Pacote codificar(Celula celula) {
        Pacote pacote = new Pacote();
        byte[] conteudo = pacote.getConteudo();

        //armazena coordenada x
        byte[] shortEmByte = shortParaArrayDeBytes(celula.getX());
        conteudo[0] = shortEmByte[0];
        conteudo[1] = shortEmByte[1];
        //armazena coordenada y
        shortEmByte = shortParaArrayDeBytes(celula.getY());
        conteudo[2] = shortEmByte[0];
        conteudo[3] = shortEmByte[1];
        //armazena status
        conteudo[4] = celula.getStatus();
        //monta o pacote
        pacote.setCabecalho(IdentificadorPacote.CELULA.getCodigo());
        pacote.setConteudo(conteudo);
        return pacote;
    }

    public Pacote codificar(IdentificadorPacote identificador, int objetivo) {

        Pacote pacote = new Pacote();
        byte[] conteudo = pacote.getConteudo();
        //armazena o valor do objetivo
        byte[] intEmArrayDeByte = intParaArrayDeBytes(objetivo);
        conteudo[0] = intEmArrayDeByte[0];
        conteudo[1] = intEmArrayDeByte[1];
        conteudo[2] = intEmArrayDeByte[2];
        conteudo[3] = intEmArrayDeByte[3];
        //monta o pacote
        pacote.setCabecalho(identificador.getCodigo());
        pacote.setConteudo(conteudo);
        return pacote;
    }

    public Pacote[] codificar(String log) {
        if (log.length() == 0) {
            throw new IllegalArgumentException("O texto deve ter 1 ou mais caracteres");
        }
        byte qtdePacote = (byte) (log.length() % 14 == 0 ? log.length() / 14 : log.length() / 14 + 1);
        Pacote[] pacotes = new Pacote[qtdePacote];
        int inicioConteudo = 0;
        int fimConteudo = 14;
        for (int ordemPacote = 0; ordemPacote < qtdePacote; ordemPacote++) {
            pacotes[ordemPacote] = new Pacote();
            pacotes[ordemPacote].setCabecalho(IdentificadorPacote.LOG.getCodigo());

            if (ordemPacote == qtdePacote - 1) {//Se for ultimo pacote
                pacotes[ordemPacote].setConteudo(stringParaArrayDeBytes(log.substring(inicioConteudo, log.length())));
                pacotes[ordemPacote].setRodape(IdentificadorPacote.FIM_SEQUENCIA_DE_PACOTES.getCodigo());
            } else {//Para os demais pacotes
                pacotes[ordemPacote].setConteudo(stringParaArrayDeBytes(log.substring(inicioConteudo, fimConteudo)));
            }

            inicioConteudo = fimConteudo;
            fimConteudo += 14;
        }
        return pacotes;
    }

    //======================== Decodifica Pacote ===============================
    public Celula decodificaCelula(Pacote pacote) {
        Celula celula = new Celula();
        byte[] conteudo = pacote.getConteudo();
        byte[] shortEmArrayDeByte = new byte[2];
        //recupera a coordenada x
        shortEmArrayDeByte[0] = conteudo[0];
        shortEmArrayDeByte[1] = conteudo[1];
        celula.setX(arrayDeBytesParaShort(shortEmArrayDeByte));
        //recupera a coordenada x
        shortEmArrayDeByte[0] = conteudo[2];
        shortEmArrayDeByte[1] = conteudo[3];
        celula.setY(arrayDeBytesParaShort(shortEmArrayDeByte));
        //recupera o status
        celula.setStatus(conteudo[4]);
        return celula;
    }

    public int decodificaObjetivo(Pacote pacote) {
        return arrayDeBytesParaInt(pacote.getConteudo());
    }

    public String decodificaLog(Pacote pacote) {
        //Construtor da classe String transforma byte[] em caracteres
        return new String(pacote.getConteudo());
    }

    //========================== Conversões  ==========================
    private byte[] intParaArrayDeBytes(int numero) {
        byte[] dados = new byte[4];
        dados[0] = (byte) ((numero >> 24));//byte mais significativo
        dados[1] = (byte) ((numero >> 16) & 0xFF);
        dados[2] = (byte) ((numero >> 8) & 0xFF);
        dados[3] = (byte) (numero & 0xFF);//byte menos significativo
        return dados;
    }

    private int arrayDeBytesParaInt(byte[] dados) {
        int numero;
        numero = dados[0] << 24;//byte mais significativo
        numero |= (dados[1] & 0xff) << 16;
        numero |= (dados[2] & 0xff) << 8;
        numero |= (dados[3] & 0xff);//byte menos significativo
        return numero;
    }

    private byte[] shortParaArrayDeBytes(short numero) {
        byte[] dados = new byte[2];
        dados[0] = (byte) (numero >> 8);//byte mais significativo
        dados[1] = (byte) (numero & 0xff);//byte menos significativo
        return dados;
    }

    private short arrayDeBytesParaShort(byte[] dados) {
        short numero;
        numero = (short) (dados[0] << 8);//byte mais significativo
        numero |= (dados[1] & 0xff);//byte menos significativo
        return numero;
    }

    private byte[] stringParaArrayDeBytes(String texto) {
        if (texto.length() > 14) {
            throw new IllegalArgumentException("O texto deve ter no máximo 14 caracteres");
        }
        byte[] conteudo = new byte[14];
        for (int i = 0; i < texto.length(); i++) {
            conteudo[i] = (byte) texto.charAt(i);
        }
        return conteudo;
    }
}